#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# :CREATED: [2018-01-25 四 22:17]
# :AUTHOR: zb
from untils import log, encode_code
import json


def save(data, path):
    """
    Keyword Arguments:
    path --
    data -- save data
    """
    log('path:{}\nsave data:{}\n'.format(path, data), level='debug')
    with open(path, 'w+') as wf:
        json.dump(data, wf, ensure_ascii=False, indent=4)
    return


def load(path):
    """
    Keyword Arguments:
    path --
    """
    with open(path, 'r') as rf:
        d = json.load(rf)
    return d


class Models(object):
    def __init__(self, form):
        """
        Keyword Arguments:
        form -- all data
        """
        for k, v in form.items():
            setattr(self, k, v)

    @classmethod
    def new(self, form):
        """
        Keyword Arguments:
        form --  a dict
        """
        return self(form)

    @classmethod
    def db_path(cls, ):
        """
        return db path
        """
        className = cls.__name__
        path = 'data/{}.txt'.format(className)
        return path

    @classmethod
    def _find_original_by(cls):
        """
        like find_by(username='tom', id='1')
        return is a region data , it is all data meet the query
        """
        all_data = load(cls.db_path())
        kwags = cls.__findBy_kwags

        def _filter(k, v, datas):
            tmp = []
            for data in datas:
                if data.get(k, None) is not None and data.get(k) == v:
                    tmp.append(data)
            return tmp

        if kwags != {}:
            for k, v in kwags.items():
                all_data = _filter(k, v, all_data)
        meet_d = all_data
        return meet_d

    @classmethod
    def find_by(cls, **kwags):
        """
        like find_by(username='tom', id='1')
        return  all instances meet the query
        """
        cls.__findBy_kwags = kwags
        meeted = cls._find_original_by()
        if meeted != []:
            instances = [cls.new(m) for m in meeted]
            return instances
        else:
            return []

    @classmethod
    def all(cls, ):
        """
        return all instance
        """
        all_data = load(cls.db_path())
        instances = [cls.new(m) for m in all_data]
        return instances

    def save(self):
        """
        Keyword Arguments:
        """
        models = self.all()
        models.append(self)
        save_data = [m.__dict__ for m in models]
        save(save_data, self.db_path())
        return

    def __repr__(self):
        """
        return all property of instance
        """
        classname = self.__class__.__name__
        properties = [
            '{}: ({})'.format(k, v) for k, v in self.__dict__.items()
        ]
        s = '\n'.join(properties)
        return '< {}\n{} >\n'.format(classname, s)


class User(Models):
    def __init__(self, form):
        """
        Keyword Arguments:
        form --
        """
        self.username = form.get('username', '游客')
        self.passwd = form.get('passwd', '')
        self.id = form.get('id', -1)

    @classmethod
    def new(self, form):
        """
        Keyword Arguments:
        form --  a dict
        """
        return self(form)

    def set_id(self):
        """
        if user has not id ,set a new id
        """
        if self.id == -1:
            self.id = self.get_new_user_id()
        else:
            pass

    def save(self):
        """
        save user
        """
        models = self.all()
        self.set_id()
        for u in models:
            if self.id == u.id:
                u.__init__(self.__dict__)
                break
        else:
            models.append(self)
        save_data = [m.__dict__ for m in models]
        save(save_data, self.db_path())
        return

    def get_new_user_id(self):
        users = self.all()
        tmp_id = 0
        for u in users:
            if u.id >= tmp_id:
                tmp_id = u.id
        user_id = int(tmp_id) + 1
        return user_id

    def validate_login(self):
        """
        check if user is legal
        """
        users = self.find_by(username=self.username)
        flg = False
        if users != []:
            for u in users:
                if self.passwd == u.passwd:
                    self.__init__(u.__dict__)
                    flg = True
        else:
            flg = False
        return flg

    def validate_resigter(self):
        """
        check if user is exist

        """
        users = self.find_by(username=self.username)
        if users != []:
            flg = True
        else:
            flg = False
        return flg


def test_load(data):
    path = 'data/users.txt'
    save(data, path)
    d = load(path)
    assert d == data
    save([], path)


def test_save(data):
    path = 'data/users.txt'
    save(data, path)
    with open(path, 'r') as rf:
        d = json.load(rf)
    assert d == data
    save([], path)


def test_models_save(data):
    new_data = {
        'passwd': 'zb',
        'username': '好',
        'id': 0,
    }
    m = Models.new(new_data)
    save([], m.db_path())
    m.save()
    d = load(m.db_path())
    assert d == data
    save([], m.db_path())


def test_models_path(data):
    m = Models.new({})
    assert 'data/Models.txt' == m.db_path()


def test_models_all(data):
    path = 'data/Models.txt'
    data.append({
        'passwd': 'bz',
        'username': 'zb',
        'id': 1,
    })
    save(data, path)
    m_all = Models.all()
    assert len(m_all) == 2
    assert sorted([x.username for x in m_all]) == sorted(['zb', '好'])
    assert sorted([x.passwd for x in m_all]) == sorted(['zb', 'bz'])
    assert sorted([x.id for x in m_all]) == sorted([0, 1])
    for x in m_all:
        assert x.username in ('zb', '好')
        assert x.passwd in ('zb', 'bz')
        assert x.id in (0, 1)
    save([], path)


def test_models_repr(data):
    new_data = {
        'passwd': 'bz',
        'username': 'zb',
        'id': 1,
    }
    m = Models.new(new_data)
    print(m)
    print(m.__dict__)


def test_user_new(data):
    data = [
        {
            "id": 0,
            "username": "好",
            "passwd": "zb"
        },
    ]
    new_data = {
        'passwd': 'zb',
        'username': '好',
        'id': 0,
    }

    u = User.new(new_data)
    save(data, u.db_path())
    print(u)
    u.save()
    # 保存时自动校验 user的id是否正确
    print('u.id', u.id)
    assert u.username == '好'
    assert u.passwd == 'zb'
    assert u.id == 1
    save([], u.db_path())

    new_data = {
        'passwd': 'zb',
        'username': '好',
    }
    save(data, u.db_path())
    u = User.new(new_data)
    print(u)
    assert u.username == '好'
    assert u.passwd == 'zb'
    assert u.id is None
    save([], u.db_path())


def test_models_find_by(data):
    """
    Keyword Arguments:
    data --
    """
    data = [
        {
            "id": 0,
            "username": "好",
            "passwd": "zb",
            "group": "zzz",
            "message": "test",
        },
        {
            "id": 2,
            "username": "好1",
            "message": "test",
            "group": "zzz",
            "passwd": "zb",
        },
        {
            "id": 1,
            "username": "好2",
            "message": "test",
            "passwd": "zb",
            "group": "zzz1",
        },

    ]
    save(data, Models.db_path())
    u = Models.find_by(username='zb')
    assert u == []
    u_l = Models.find_by(username='好')
    u = u_l.pop()
    assert u.id == 0
    u_l = Models.find_by(message='test')
    assert len(u_l) == 3
    u_l = Models.find_by(message='test', group='zzz')
    print(u_l)
    assert len(u_l) == 2


def test_user_validate_login(data):
    data = [
        {
            'passwd': 'zb',
            'username': '好',
            'id': 0,
        },
    ]
    new_data = {
        'passwd': 'bz',
        'username': 'zb',
        'id': 1,
    }
    u = User.new(new_data)
    save(data, u.db_path())
    print('validate_login---1---')
    assert u.validate_login() is False
    u.username = '好'
    u.passwd = 'zb'

    print('validate_login---2---')
    assert u.validate_login() is True
    u.username = '好'
    u.passwd = '1'

    print('validate_login---3---')
    assert u.validate_login() is False
    save([], u.db_path())


def test_user_validate_resigter(data):
    data = [
        {
            'passwd': 'zb',
            'username': '好',
            'id': 0,
        },
    ]
    new_data = {
        'passwd': 'bz',
        'username': 'zb',
        'id': 1,
    }
    u = User.new(new_data)
    save(data, u.db_path())
    assert u.validate_resigter() == False
    u.username = '好'
    assert u.validate_resigter() == True
    save([], u.db_path())


def main():
    path = 'data/users.txt'
    data = [
        {
            'passwd': 'zb',
            'username': '好',
            'id': 0,
        },
    ]
    from untils import _log
    import logging
    _log.setLevel(logging.ERROR)

    print('------- test test_load --------')
    test_load(data)
    print('------- test test_save--------')
    test_save(data)
    print('------- test test_models_save--------')
    test_models_save(data)
    print('------- test test_models_path --------')
    test_models_path(data)
    print('------- test test_models_all --------')
    test_models_all(data)
    print('------- test test_models_repr --------')
    test_models_repr(data)
    print('------- test test_user_new --------')
    test_user_new(data)
    print('------- test test_user_validate_login --------')
    test_user_validate_login(data)
    print('------- test test_models_find_by --------')
    test_models_find_by(data)
    print('------- test test_user_validate_resigter--------')
    test_user_validate_resigter(data)


if __name__ == '__main__':
    main()
